#include "GLTexture2D.h"

#include <Rz/Graphics/Render/IRenderDevice.h>
#include <Rz/Graphics/Render/IBuffer.h>
#include <Rz/Graphics/Render/Libraries/OpenGL/GL.h>

namespace Rz { namespace Graphics { namespace Render { namespace Libraries { namespace OpenGL {

GLTexture2D::GLTexture2D(IRenderDevice* renderDevice)
	: _width(0)
	, _height(0)
	, _mipMap(true)
	, _surfaceFormat(SurfaceFormat::Color)
{
	this->SetRenderDevice(renderDevice);

	_handle = GL::GenTexture();
	this->Init(_surfaceFormat, _width, _height, NULL);
	GL::GenerateMipmap(GL_TEXTURE_2D);
}

GLTexture2D::GLTexture2D(IRenderDevice* renderDevice, u32 width, u32 height)
	: _width(width)
	, _height(height)
	, _mipMap(true)
	, _surfaceFormat(SurfaceFormat::Color)
{
	this->SetRenderDevice(renderDevice);

	_handle = GL::GenTexture();
	this->Init(_surfaceFormat, _width, _height, NULL);
	GL::GenerateMipmap(GL_TEXTURE_2D);
}

GLTexture2D::GLTexture2D(IRenderDevice* renderDevice, u32 width, u32 height, bool mipMap, SurfaceFormat surfaceFormat)
	: _width(width)
	, _height(height)
	, _mipMap(mipMap)
	, _surfaceFormat(surfaceFormat)
{
	this->SetRenderDevice(renderDevice);

	_handle = GL::GenTexture();
	this->Init(_surfaceFormat, _width, _height, NULL);
	GL::GenerateMipmap(GL_TEXTURE_2D);
}

GLTexture2D::~GLTexture2D()
{
	GL::DeleteTexture(_handle);
}

void GLTexture2D::Bind()
{
	GL::BindTexture(GL_TEXTURE_2D, _handle);
}

void GLTexture2D::Unbind()
{
	GL::BindTexture(GL_TEXTURE_2D, 0);
}

void GLTexture2D::Init(SurfaceFormat surfaceFormat, u32 width, u32 height, const void* data)
{
	_surfaceFormat = surfaceFormat;
	_width = width;
	_height = height;

	SurfaceToPixelFormat(_surfaceFormat, &_glInternalFormat, &_glFormat, &_glType);

	SetData(data, 0);
}

void GLTexture2D::SetData(const void* data, u32 dataLength, u32 startIndex, u32 offsetInBytes)
{
	const u8* charData = static_cast<const u8*>(data);
	charData += startIndex;

	RZ_UNUSED(offsetInBytes);
	RZ_UNUSED(dataLength);

	this->Bind();
	GL::TexImage2D(GL_TEXTURE_2D, 0, _glInternalFormat, _width, _height, _glFormat, _glType, static_cast<const GLvoid*>(charData));
}

void GLTexture2D::GetData(void* data, u32 count, u32 startIndex) const
{
	// TODO: NotImplemented

	RZ_UNUSED(data);
	RZ_UNUSED(count);
	RZ_UNUSED(startIndex);
}

GLuint GLTexture2D::GetTextureHandle() const
{
	return _handle; 
}

void GLTexture2D::SurfaceToPixelFormat(SurfaceFormat surfaceFormat, GLenum* internalFormat, GLenum* format, GLenum* type)
{
	*internalFormat = GL_RGBA;
	*format = GL_RGBA;
	*type = GL_UNSIGNED_BYTE;

	switch (surfaceFormat)
	{
		default:
		case SurfaceFormat::HdrBlendable:
		case SurfaceFormat::NormalizedByte2:
		case SurfaceFormat::Dxt1:
		case SurfaceFormat::Dxt3:
		case SurfaceFormat::Dxt5:
		case SurfaceFormat::Color:
		case SurfaceFormat::NormalizedByte4:
			*internalFormat = GL_RGBA;
			*format = GL_RGBA;
			*type = GL_UNSIGNED_BYTE;
			break;

		case SurfaceFormat::Bgr565:
			*internalFormat = GL_RGB;
			*format = GL_RGB;
			*type = GL_UNSIGNED_SHORT_5_6_5;
			break;

		case SurfaceFormat::Bgra4444:
			*internalFormat = GL_RGBA4;;
			*format = GL_RGBA;
			*type = GL_UNSIGNED_SHORT_4_4_4_4;
			break;

		case SurfaceFormat::Bgra5551:
			*internalFormat = GL_RGBA;
			*format = GL_RGBA;
			*type = GL_UNSIGNED_SHORT_5_5_5_1;
			break;

		case SurfaceFormat::Alpha8:
			*internalFormat = GL_LUMINANCE;
			*format = GL_LUMINANCE;
			*type = GL_UNSIGNED_BYTE;
			break;

		case SurfaceFormat::Single:
			*internalFormat = GL_R32F;
			*format = GL_RED;
			*type = GL_FLOAT;
			break;

		//////////////////////////////////////////////////////////////////////////

		case SurfaceFormat::HalfVector2:
			*internalFormat = GL_RG16F;
			*format = GL_RG;
			*type = GL_FLOAT;
			break;

		case SurfaceFormat::Rgba64:
		case SurfaceFormat::HalfVector4:
			*internalFormat = GL_RGBA16F;
			*format = GL_RGBA;
			*type = GL_FLOAT;
			break;

		case SurfaceFormat::Vector2:
		case SurfaceFormat::Rg32:
			*internalFormat = GL_RG32F;
			*format = GL_RED;
			*type = GL_FLOAT;
			break;

		case SurfaceFormat::Rgba1010102:
			*internalFormat = GL_RGB10_A2;
			*format = GL_RGBA;
			*type = GL_FLOAT;
			break;

		case SurfaceFormat::HalfSingle:
			*internalFormat = GL_R16F;
			*format = GL_RED;
			*type = GL_FLOAT;
			break;

		case SurfaceFormat::Vector4:
			*internalFormat = GL_RGBA32F;
			*format = GL_RGBA;
			*type = GL_FLOAT;
			break;
	}
}

} } } } }

